home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Aminet 52
/
Aminet 52 (2002)(GTI - Schatztruhe)[!][Dec 2002].iso
/
Aminet
/
misc
/
emu
/
Apex-src.lha
/
FLOPHANA.68K
< prev
next >
Wrap
Text File
|
2001-09-30
|
53KB
|
1,987 lines
;FLOPHANA.68K JUN-15-88 (Also see INFOSTR)
;Amiga 3.5" Disk Handler
; by Loren Blaney
;
;REVISION HISTORY:
;MAR-11-87, Original
;JUN-15-88, Eliminate unnecessary delays waiting for motor spin up and
; initialize CIAB data direction register.
;
;WARNING:
;This currently works only for reading/writing an even number of blocks.
; The local variables should be in a stack structure, not at $D00.
;
;NOTES:
;This is based on a disassembly of the Amiga Kickstart "ROM" Kernel,
; V1.1, trackdisk.device 31.58 (23 Nov 1985).
;For further information see section 8.3 and Appendix A in the Amiga
; Hardware Manual, and see III-3 (Trackdisk) and Appendix L in the ROM
; Kernel Manual.
;
;The register convention used here is different than the convention
; generally used by Apex. The registers D0, D1, A0, and A1 are used
; as scratch. All other registers have their values preserved across
; subroutine calls.
;
;Apex blocks have 256 bytes; Amiga sectors have 512 bytes. "End
; buffering" is used to handle half sectors.
;
;INPUTS:
; UNIT: Unit number
; FADDR: Address in memory to read/write (long word)
; BLKNOX: Block on the disk to start read/write
; NBLKS: Number of blocks to read/write
;
;
;DISK FORMAT:
; Double sided, 80 cylinders, 160 tracks
;
;TRACK FORMAT:
; 11 sectors, gap
; (The gap is written as nulls. There are no gaps between sectors)
;
;SECTOR FORMAT:
; All data is MFM encoded (Amiga version).
; 0 00 (MFM = AAAA)
; 1 00 (MFM = AAAA)
; 2 A1 Standard sync byte -- MFM encoded $A1 with a
; 3 A1 clock pulse missing to distinguish it from
; data (= $4489 each)
; 4 FF Format byte (Amiga 1.0)
; 5 1 byte Track number
; 6 1 byte Sector number
; 7 1 byte Number of sectors until end of write (1-11)
; 8 16 bytes Header label -- OS recovery info (not used)
;24 4 bytes Header "checksum" (bytes (4-23)
;28 4 bytes Data "checksum"
;32 512 bytes Data
; (544 bytes total)
;
;"Checksums" are really just a simple exclusive-oring of the unencoded
; 16-bit words (i.e. parity check).
NOLIST
INCLUDE SYSPAG
LIST
;----------------------------------------------------------------------
;DEFINITIONS:
;
UNTNUM EQU 0 ;Unit number of first floppy
CIAB EQU $BFD000 ;CIAB base address
CIABB EQU $BFD100 ;CIAB PRB address (= CIAB + PRB)
;8520-A OUTPUTS:
;7 -DSKMOTOR Motor on (latched)
;6 -DSKSEL3 Select drive 3
;5 -DSKSEL2 Select drive 2
;4 -DSKSEL1 Select drive 1
;3 -DSKSEL0 Select drive 0
;2 DSKSIDE Select lower side
;1 DSKDIREC Step outward (toward 0)
;0 -DSKSTEP Step head
CIAA EQU $BFE001 ;Base address of 8520-A
; INPUTS:
;5 -DSKRDY Disk is ready
;4 -DSKTRACK0 On track zero
;3 -DSKPROT Write protected
;2 -DSKCHANGE Disk was changed
DDRB EQU $300 ;Offset to CIA data direction register B
IOBASE EQU $DFF000 ;Base address of I/O
DMACONR EQU $DFF002 ;DMA control register (read)
INTREQR EQU $DFF01E ;Interrupt request bits (read)
DSKPTH EQU $DFF020 ;Pointer to disk buffer (high 3 bits)
DSKPTL EQU $DFF022 ;Pointer to disk buffer (low 16 bits)
DSKLEN EQU $DFF024 ;Number of words to transfer and
; bit 15 = DMA enable, bit 14 = write
BLTCON0 EQU $DFF040 ;Blitter control register 0
BLTCON1 EQU $DFF042 ;Blitter control register 1
BLTAFWM EQU $DFF044 ;Blitter first word mask for source A
BLTBPTH EQU $DFF04C ;Blitter pointer for source B
BLTAPTH EQU $DFF050 ;Blitter pointer for source A
BLTDPTH EQU $DFF054 ;Blitter pointer for source D
BLTSIZE EQU $DFF058 ;Blitter start and size register
BLTBMOD EQU $DFF062 ;Blitter modulo for source B
INTENA EQU $DFF09A ;Interrupt enable bits
INTREQ EQU $DFF09C ;Interrupt request bits
ADKCON EQU $DFF09C ;Audio, disk UART control
;Register A1 points to a standard I/O request block whos format is:
; IOStdReq A1
;
; Message 0 Not used
; io_Device 20 Device/unit/drive number (0..3)
; io_Unit 24 Pointer to local variables structure
; io_Command 28 Read = 2; write = 3
; io_Flags 30 Not used
; io_Error 31 Error code (20..35)
; io_Actual 32 Actual number of bytes read/written
; io_Length 36 Requested number of bytes to read/write
; io_Data 40 Pointer to memory data to read/write
; io_Offset 44 Offset in bytes to track and sector
;
; ioTD_Count 48 IOExtTD, For extended I/O which is not
; ioTD_SecLabel 52 used here
;
;----------------------------------------------------------------------
;
ORG $D00 ;*** THESE SHOULD NOT BE HERE *** ???
;These variables have replaced the io_Unit structure:
FLAGS DS.B 1 ;38 ;Bit array
;0: Sector label in extended I/O is used
;1: No disk in drive
;2: Extended I/O is used
;3: (set and cleared but not used)
;4: Disk is write-protected (not used)
CIABBX DS.B 1 ;39 ;Copy of CIAB-B port
CNTR DS.B 1 ;40 ;Counter
DS.B 1
IOPTR DS.L 1 ;42 ;Pointer to IOStdReq structure
DS.B 1
SECTOR DS.B 1 ;47 ;Current sector number
TRACKD DS.W 1 ;48 ;Desired track number
;GLOBAL VARIABLE:
TRACKS DS.W 4 ;50 ;Table of track numbers (for each unit)
;*** WARNING, THIS SHOULD BE INITIALIZED TO -1, OR THERE IS A SLIGHT
; CHANCE THAT A DISK WILL GET EATEN. ***
TRACK DS.W 1 ;Track number for current unit
PTR2 DS.L 1 ;58 ;Pointer into track buffer
PTR1 DS.L 1 ;62 ;Pointer into track buffer
IODATA DS.L 1 ;66 ;Pointer to unencoded I/O data
LBLBUF DS.L 1 ;70 ;Pointer to sector label data region
SECBUF DS.L 128 ;Sector buffer, blocks into sectors
ORG $70000 ;????????
TRKBUF DS.B $4004 ;MFM Encoded track buffer
;----------------------------------------------------------------------
;
ORG MEMTOP - $2800
START EQU @ ;Address where this handler starts
FLOPHAN DC.L DUMMY ;0
DC.L DUMMY ;1
DC.L READ ;2
DC.L WRITE ;3
DC.L DUMMY ;4
DC.L GETINFO ;5
DC.L DUMMY ;6 (Spare)
DC.L DUMMY ;7 (Spare)
;-----------------------------------------------------------------------
;Read bytes from floppy and store them into memory.
; This deals with the problem of reading 256-byte blocks from 512-byte
; sectors by doing "end buffering".
;
READ MOVEM.L D0-D7/A0-A6,-(SP) ;Save all registers
BSR OPEN ;Initialize track buffer, etc.
MOVE.L BLKNOX,D2 ;Get arguments
MOVEA.L FADDR,A2
MOVE.L NBLKS,D3
BTST #0,D2 ;Is BLKNOX an odd number?
BEQ.S RD20 ;Branch if not
SUBQ.L #1,D2 ;Backup to previous (even) block
LEA SECBUF,A2 ;Read 2 blocks into the sector buffer
MOVEQ #2,D3
BSR DOREAD ;READ(BLKNOX, FADDR, NBLKS)
; D2 A2 D3
LEA SECBUF,A0 ;Move the second block in the sector
LEA $100(A0),A0 ; buffer into memory at FADDR
MOVEA.L FADDR,A2
MOVEQ #64-1,D0
RD10 MOVE.L (A0)+,(A2)+ ;Do it 4 bytes at a time
DBF D0,RD10
;(A2 [FADDR] is incremented by 256)
ADDQ.L #2,D2 ;Net gain of one block (BLKNOX)
MOVE.L NBLKS,D3 ;One less block to do
SUBQ.L #1,D3
RD20
MOVEQ #FALSE,D4 ;Anticipate an even number of remaining
BTST #0,D3 ; blocks
BEQ.S RD30 ;Branch if correct
MOVEQ #TRUE,D4 ;Set flag to handle last, odd block
SUBQ.L #1,D3 ;Decrement NBLKS to make them even
MOVE.L D3,D0 ;FADDR' := FADDR + NBLKS *256
ASL.L #8,D0 ; A3 A2 D3
LEA 0(A2,D0.L),A3
RD30
TST.L D3 ;Are there some blocks to read?
BEQ.S RD40 ;Branch if not
BSR DOREAD ;READ(BLKNOX, FADDR, NBLKS)
RD40 ; D2 A2 D3
TST.W D4 ;Is there a last, odd block?
BEQ.S RD60 ;Branch if not
LEA SECBUF,A2 ;Read 2 blocks into the sector buffer
ADD.L D3,D2 ;BLKNOX:= BLKNOX + NBLKS
MOVEQ #2,D3
BSR DOREAD ;READ(BLKNOX, FADDR, NBLKS)
; D2 A2 D3
LEA SECBUF,A0 ;Move the first block in the sector
MOVEQ #64-1,D0 ; buffer into memory (FADDR')
RD50 MOVE.L (A0)+,(A3)+ ;Do it 4 bytes at a time
DBF D0,RD50
RD60
BSR MTROFF ;Turn off the motor
MOVEM.L (SP)+,D0-D7/A0-A6 ;Restore all registers
DUMMY RTS
;-----------------------------------------------------------------------
;Write bytes from memory onto the floppy.
; This deals with the problem of writing 256-byte blocks on to 512-byte
; sectors by doing "end buffering".
;
WRITE MOVEM.L D0-D7/A0-A6,-(SP) ;Save all registers
BSR OPEN ;Initialize track buffer, etc.
MOVE.L BLKNOX,D2 ;Get arguments
MOVEA.L FADDR,A2
MOVE.L NBLKS,D3
BTST #0,D2 ;Is BLKNOX an odd number?
BEQ.S WR20 ;Branch if not
SUBQ.L #1,D2 ;Backup to previous (even) block
LEA SECBUF,A2 ;Read 2 blocks into the sector buffer
MOVEQ #2,D3
BSR DOREAD ;READ(BLKNOX, FADDR, NBLKS)
; D2 A2 D3
MOVEA.L FADDR,A0
LEA SECBUF,A1 ;Move memory (at FADDR) into the second
LEA $100(A1),A1 ; block in the sector buffer
MOVEQ #64-1,D0 ;Move 64 longs
WR10 MOVE.L (A0)+,(A1)+ ;Do it 4 bytes at a time
DBF D0,WR10
BSR DOWRITE ;WRITE(BLKNOX, FADDR, NBLKS)
; D2 A2 D3
MOVEA.L A0,A2 ;(A2 [FADDR] is incremented by 256)
ADDQ.L #2,D2 ;Net gain of one block (BLKNOX)
MOVE.L NBLKS,D3 ;One less block to do
SUBQ.L #1,D3
WR20
MOVEQ #FALSE,D4 ;Anticipate an even number of remaining
BTST #0,D3 ; blocks
BEQ.S WR30 ;Branch if correct
MOVEQ #TRUE,D4 ;Set flag to handle last, odd block
SUBQ.L #1,D3 ;Decrement NBLKS to make them even
MOVE.L D3,D0 ;FADDR' := FADDR + NBLKS *256
ASL.L #8,D0 ; A3 A2 D3
LEA 0(A2,D0.L),A3
WR30
TST.L D3 ;Are there some blocks to WRITE?
BEQ.S WR40 ;Branch if not
BSR DOWRITE ;WRITE(BLKNOX, FADDR, NBLKS)
WR40 ; D2 A2 D3
TST.W D4 ;Is there a last, odd block?
BEQ.S WR60 ;Branch if not
LEA SECBUF,A2 ;Read 2 blocks into the sector buffer
ADD.L D3,D2 ;BLKNOX:= BLKNOX + NBLKS
MOVEQ #2,D3
BSR DOREAD ;READ(BLKNOX, FADDR, NBLKS)
; D2 A2 D3
MOVEA.L A3,A0
LEA SECBUF,A1 ;Move 256 bytes from memory (at FADDR')
MOVEQ #64-1,D0 ; into the first half of the sector buf
WR50 MOVE.L (A0)+,(A1)+ ;Do it 4 bytes at a time
DBF D0,WR50
BSR DOWRITE ;WRITE(BLKNOX, FADDR, NBLKS)
WR60 ; D2 A2 D3
BSR CLOSE ;Write anything left in the track buffer
MOVEM.L (SP)+,D0-D7/A0-A6 ;Restore all registers
RTS
;-----------------------------------------------------------------------
;Return the address of the information block in D0
;
GETINFO MOVE.L #INFO,D0
RTS
INFO DC.L START ;Handler start and end addresses
DC.L END
DC.L INFOSTR
INFOSTR ASCII 'FLOPHANA JUN-15-88 3 1/2", 3520 blocks'
DC.B 0
;======================================================================
;SUBROUTINES
;-----------------------------------------------------------------------
;Read bytes from floppy and store them into memory.
;
; Inputs:
; D2 = (BLKNOX_) Starting Apex block number (must be even)
; A2 = (FADDR_) Starting memory address to read/write
; D3 = (NBLKS_) Number of Apex blocks (must be even)
;
DOREAD MOVEM.L D0-D7/A0-A6,-(SP) ;Save registers
LINK A5,#-56 ;Reserve space for IOStdReq structure
MOVEA.L SP,A1 ;Point to base of IOStdReq structure
BSR SETUP ;Initialize IOStdReq structure
MOVE.W #2,28(A1) ;Indicate "read"
BSR CMD_RW ;Call disk I/O handler
BSR REPORT ;Report any errors
UNLK A5 ;Release structure space
MOVEM.L (SP)+,D0-D7/A0-A6 ;Restore registers
RTS
;-----------------------------------------------------------------------
;Write bytes from memory onto the floppy
;
; Inputs:
; D2 = (BLKNOX_) Starting Apex block number (must be even)
; A2 = (FADDR_) Starting memory address to read/write
; D3 = (NBLKS_) Number of Apex blocks (must be even)
;
DOWRITE MOVEM.L D0-D7/A0-A6,-(SP) ;Save registers
LINK A5,#-56 ;Reserve space for IOStdReq structure
MOVEA.L SP,A1 ;Point to base of IOStdReq structure
BSR SETUP ;Initialize IOStdReq structure
MOVE.W #3,28(A1) ;Indicate "write"
BSR CMD_RW ;Call disk I/O handler
BSR REPORT ;Report any errors
UNLK A5 ;Release structure space
MOVEM.L (SP)+,D0-D7/A0-A6 ;Restore registers
RTS
;----------------------------------------------------------------------
;Routine to set up the IOStdReq structure.
; Inputs:
; A1 = Pointer to IOStdReq structure
; D2 = (BLKNOX_) Starting Apex block number (must be even)
; A2 = (FADDR_) Starting memory address to read/write
; D3 = (NBLKS_) Number of Apex blocks (must be even)
;
; Outputs:
; PTR = Pointer to encoded track buffer
; FLAGS
;
;Registers D0, D1 and A0 are destroyed.
;
SETUP MOVE.W #$8250,$DFF096.L ;Enable blitter and disk DMA
MOVE.W #$0400,$DFF09E.L ;Clear WORDSYNC
MOVE.W #$8100,$DFF09E.L ;Enable fast mode
MOVE.B #0,FLAGS
MOVEA.L A1,A0
MOVEQ #55,D0 ;Clear the structure space
SUP10 CLR.B (A0)+
DBF D0,SUP10
MOVE.B UNIT,D0 ;Get unit number, 0..3
ANDI.B #$03,D0
MOVE.B D0,23(A1) ;io_Device
MOVE.L D3,D0 ;Number of bytes = NBLKS_ * 256
ASL.L #8,D0
MOVE.L D0,36(A1) ;io_Length
MOVE.L A2,40(A1) ;io_Data
MOVE.L D2,D0 ;Offset = BLKNOX_ * 256
ASL.L #8,D0
MOVE.L D0,44(A1) ;io_Offset
RTS
;----------------------------------------------------------------------
;This routine initializes the track buffer and sets PTR1 to it.
; Registers D0, D1, and A0 are destroyed.
;
OPEN LEA CIAB.L,A0 ;Point to base of 8520-B
MOVE.B #$FF,DDRB(A0) ;Set port B to all outputs
MOVE.B #$FF,CIABBX ;Indicate that motor is off (bit 7 high)
LEA TRKBUF.L,A0 ;Track buffer ($4004 bytes)
MOVE.L A0,PTR1
MOVE.W #-1,0(A0) ;Set invalid track number
MOVE.W #0,2(A0) ;Indicate "clean" buffer
MOVE.L #$AAAAAAAA,D1 ;Fill track buffer with clock bits
MOVE.W #$FFF,D0
LEA 4(A0),A0
OP05 MOVE.L D1,(A0)+
DBF D0,OP05
RTS
;----------------------------------------------------------------------
;This routine writes the track buffer to disk if there is something
; on it.
;
CLOSE LEA TRKBUF.L,A0 ;Is the track buffer dirty?
BTST #0,2(A0)
BEQ.S CL20 ;Branch if not
MOVE.L A0,PTR2
BSR SUBRO ;Write the track buffer (D0=error code)
CL20
BSR REPORT ;Report any error
BRA MTROFF ;(PBRA) Turn off the motor
;-----------------------------------------------------------------------
;Report the error for the error code in D0.
; D0 and A6 are destroyed.
;
REPORT TST.B D0 ;Test the error code
BEQ ERR00 ;Branch if no errors
BSR MTROFF ;Turn off the motor
CMPI.B #20,D0 ;It's an unspecified error if the error
BLO ERR20 ; code is outside the range 20..35
CMPI.B #35,D0
BHI ERR20
SUBI.B #20,D0 ;Subtract offset
LSL.W #2,D0 ;Times 4 for long entries
MOVEA.W D0,A6 ;Move index into an address register
LEA ERRTBL-@-2(PC,A6),A6 ;Get the address of the table entry
MOVEA.L (A6),A6 ;Get the address of the error routine
JMP (A6) ;Jump to this address
;Table of entry points to error reporting routines
ERRTBL DC.L ERR20
DC.L ERR21
DC.L ERR22
DC.L ERR23
DC.L ERR24
DC.L ERR25
DC.L ERR26
DC.L ERR27
DC.L ERR28
DC.L ERR29
DC.L ERR30
DC.L ERR31
DC.L ERR32
DC.L ERR33
DC.L ERR34
DC.L ERR35
;-----------------------------------------------------------------------
;ERROR REPORTING ROUTINES
;
; Normal errors include:
; door open
; write protected
; damaged disk
; unformatted disk
; wrong format
; inserted wrong
;
ERR20 JSR VERROR
ASCII '20 - DISK ERROR (UNSPECIFIED)'
DC.B 0
ERR00 RTS ;(PJMPs won't work)
ERR21 JSR VERROR
ASCII '21 - NO SECTOR HEADER'
DC.B 0
RTS
ERR22 JSR VERROR
ASCII '22 - SECTOR PREAMBLE'
DC.B 0
RTS
ERR23 JSR VERROR
ASCII '23 - SECTOR ID'
DC.B 0
RTS
ERR24 JSR VERROR
ASCII '24 - HEADER CHECKSUM'
DC.B 0
RTS
ERR25 JSR VERROR
ASCII '25 - DATA CHECKSUM'
DC.B 0
RTS
ERR26 JSR VERROR
ASCII '26 - TOO FEW SECTORS'
DC.B 0
RTS
ERR27 JSR VERROR
ASCII '27 - SECTOR HEADER'
DC.B 0
RTS
ERR28 JSR VERROR
ASCII '28 - WRITE PROTECTED'
DC.B 0
RTS
ERR29 JSR VERROR
ASCII '29 - DISK CHANGED OR MISSING'
DC.B 0
RTS
ERR30 JSR VERROR
ASCII '30 - SEEK'
DC.B 0
RTS
ERR31 JSR VERROR
ASCII '31 - DISK MEMORY OVERFLOW'
DC.B 0
RTS
ERR32 JSR VERROR
ASCII '32 - DISK UNIT NUMBER'
DC.B 0
RTS
ERR33 JSR VERROR
ASCII '33 - DRIVE TYPE'
DC.B 0
RTS
ERR34 JSR VERROR
ASCII '34 - DISK IN USE'
DC.B 0
RTS
ERR35 JSR VERROR
ASCII '35 - DISK - UNASSIGNED'
DC.B 0
RTS
;----------------------------------------------------------------------
;This routine turns off the motor for the current unit.
;
MTROFF MOVE.L D0,-(SP) ;Save D0
MOVE.B #$FF,CIABBX ;Turn off the motor
MOVE.B CIABBX,CIABB.L ; (this also selects the lower head)
MOVEQ #3,D0 ;Select units 0..3 which correspond to
ADD.B UNIT,D0 ; bits 3..6
BCLR D0,CIABBX
MOVE.B CIABBX,CIABB.L
BSET D0,CIABBX ;Deselect unit
MOVE.B CIABBX,CIABB.L
MOVE.L (SP)+,D0 ;Restore D0
RTS
;======================================================================
;FLOPPY DISK ROUTINES MODIFIED FROM ROM KERNEL CODE:
;----------------------------------------------------------------------
;Read or write I/O data from or to the disk.
;
;Inputs:
; A1 = IOStdReq (the significant entries are shown):
; io_Command = 2=Read, 3=Write
; io_Length = Number of bytes to read/write (must be an
; integer multiple of 256)
; io_Data = Pointer to (unencoded) I/O data
; io_Offset = Byte address of disk location (= sector *
; cylinder * 2 * 512)
; PTR1 = Pointer to track buffer
; TRACKS = Array of current head positons for each unit
;
;Outputs:
; D0 = Error code
;
;
CMD_RW MOVEM.L D2/A2-A4,-(SP) ;Save registers
MOVEA.L 24(A1),A3 ;Get io_Unit structure (this has been
MOVEA.L A1,A2 ; replaced by variable names)
MOVE.L A2,IOPTR ;Save pointer to IOStdReq structure
;Set port control bits (CIABBX) and TRACK for current unit:
MOVE.B CIABBX,D0 ;Select drive (= unit = device)
ORI.B #$7F,D0 ;Disable all bits except DSKMOTOR
MOVE.W 22(A2),D1 ;Get io_Device (unit)
ANDI.W #$0003,D1 ;Mask for safety, legal units: 0..3
MOVE.L D1,D2 ;Save copy of unit number
ADDI.W #$0003,D1 ;CIABB bits 3..6 select units 0..3
BCLR D1,D0 ;Drive select is true low
ADD.W D2,D2 ;Get track for current unit
LEA TRACKS.L,A0 ;Index into TRACK table (words)
MOVE.W 0(A0,D2.W),D1
MOVE.W D1,TRACK ;Set track number for current unit
BTST #0,D1 ;Is this an odd or even track number?
BEQ.S LB300 ;Branch if even -- use lower side
BCLR #2,D0 ;Else, select upper head
LB300
MOVE.B D0,CIABBX ;Set copy of CIA-B port
MOVE.L #0,32(A2) ;Clear io_Actual (amount read/written)
MOVE.L 40(A2),IODATA ;Get pointer to io_Data
MOVE.L 44(A2),D0 ;Get io_Offset
MOVE.L D0,D1
ANDI.L #$000001FF,D1 ;Must be an integer multiple of 512
BNE LB50A ;Branch if not
CMPI.L #$000DC000,D0 ;Must be < 2 *80 *11 *512
BHS LB50A ;Branch if not
MOVE.L 36(A2),D1 ;Get io_Length
ADD.L D0,D1 ;Add io_Offset
CMPI.L #$000DC000,D1 ;Must be <= 2 *80 *11 *512
BHI LB50A ;Branch if not
MOVEQ #9,D1 ;Divide by 512 to get "sector" number
LSR.L D1,D0
DIVU #11,D0 ;Divide by 11 to get desired track no.
MOVE.W D0,TRACKD
SWAP D0 ;Get the remainder, the sector number
MOVE.B D0,SECTOR
BTST #2,FLAGS ;Is extended I/O used?
BEQ.S LB36E ;Branch if not
MOVE.L 52(A2),LBLBUF ;Get pointer to sector label
BEQ.S LB36E ;Branch if it is not used
BSET #0,FLAGS ;Indicate sector label is not used
LB36E
BTST #1,FLAGS ;Is there a disk inserted?
BEQ.S LB380 ;Branch if yes
MOVE.B #29,31(A2) ;io_Error: Disk changed or not present
BRA LB500 ;Exit
LB380
MOVE.W TRACKD,D0 ;Get the desired track number
BSR SUBRM ;Is this track in the buffer?
MOVE.L A0,PTR2 ;Set PTR2=A0 if not
BNE.S LB3E4 ;Branch if it is in the buffer
MOVEA.L PTR1,A0
MOVEA.L A0,A2
MOVE.L A2,PTR2 ;PTR2:= A2:= A0:= PTR1
;Read track into buffer, loop for all tracks:
LB398 BTST #0,2(A2) ;Is the current track "dirty"
BEQ.S LB3B4 ;Branch if not
BSR SUBRO ;Write the current track
TST.L D0 ;Any errors?
BEQ.S LB3B4 ;Branch if not
MOVEA.L IOPTR,A1
MOVE.B D0,31(A1) ;Set io_Error
BRA LB500 ;Exit
LB3B4
MOVE.W TRACKD,0(A2) ;Get desired track number
BCLR #0,2(A2)
CLR.B CNTR ;Initialize counter
BSR SUBRP ;Read in the desired track
MOVE.B 3(A2),D0
CMPI.B #11,D0 ;Any bad errors?
BLO.S LB3E4 ;Branch if not
MOVE.W #$FFFF,0(A2)
MOVEA.L IOPTR,A1
MOVE.B D0,31(A1) ;Set io_Error
BRA LB500 ;Exit
;Track is in the buffer, loop for all sectors:
LB3E4 MOVEA.L IOPTR,A2 ;Get io_StdReq
MOVE.W 28(A2),D0 ;Get I/O command
MOVEA.L PTR2,A0
CMPI.B #$03,D0 ;Is it a write command?
BNE LB470 ;Branch if not (must be read)
;Write command:
BSET #0,2(A0)
MOVEQ #0,D0 ;Calculate offset into the track
MOVE.B SECTOR,D0 ; buffer to the desired sector
SUB.B 3(A0),D0
BPL.S LB40E
ADDI.B #11,D0
LB40E
MULU #544*2,D0 ;Multiply by encoded bytes per sector
LEA 1664(A0),A4 ;Skip the gap
ADDA.L D0,A4
BTST #0,FLAGS ;Is sector label (header) used?
BEQ.S LB446 ;Branch if not
MOVEA.L LBLBUF,A0 ;Point to label (header) data
MOVE.L #16,D0 ;Encode 16 bytes
LEA 16(A4),A1 ;A1 = location for encoded data
BSR ENCODE ;Encode and move the label data
LEA 8(A4),A0 ;Point to encoded data
MOVE.W #40,D1 ;Number of encoded bytes
BSR DOCKSUM ;Compute header checksum (D0)
LEA 48(A4),A0 ;Encode and store header checksum
BSR ENCOD_L
LB446 MOVEA.L IODATA,A0 ;Get pointer to io_Data
MOVE.L #512,D0 ;Number of bytes to encode
LEA 64(A4),A1 ;A1 = location for encoded data
BSR ENCODE ;Encode and move sector data
LEA 64(A4),A0 ;Point to start of encoded data
MOVE.W #1024,D1 ;Number of bytes of encoded data
BSR DOCKSUM ;Compute checksum (D0)
LEA 56(A4),A0 ;Encode and store data checksum
BSR ENCOD_L
BRA LB4B6 ;Go to next sector
;Enter here if read command:
LB470 MOVEQ #0,D0 ;sector number (0..10)
MOVE.B SECTOR,D0
SUB.B 3(A0),D0
BPL.S LB480
ADDI.B #11,D0
LB480
MULU #544*2,D0 ;calculate offset into track buffer
LEA 1664(A0),A4 ; to desired sector
ADDA.L D0,A4
BTST #0,FLAGS ;Is extended I/O (header) used?
BEQ.S LB4A4 ;Branch if not
LEA 16(A4),A1 ;Point to encoded label (header) data
MOVEA.L LBLBUF,A0 ;Point to place for decoded label data
MOVE.L #16,D0 ;Number of bytes to decode
BSR DECODE ;Decode and move a block of data
LB4A4
LEA 64(A4),A1 ;Point to encoded data
MOVEA.L IODATA,A0 ;Get pointer to io_Data (destination)
MOVE.L #512,D0 ;Number of bytes to decode
BSR DECODE ;Decode and move a block of data
LB4B6 MOVE.L #512,D1 ;Point to next sector
ADD.L D1,IODATA
MOVE.L 32(A2),D0 ;Bump io_Actual
ADD.L D1,D0
MOVE.L D0,32(A2)
BTST #0,FLAGS ;Is extended I/O used?
BEQ.S LB4DA ;Branch if not
ADDI.L #16,LBLBUF ;Bump label pointer
LB4DA
CMP.L 36(A2),D0 ;Compare io_Actual to io_Length
BHS.S LB500 ;Branch if we're done -- exit
MOVEA.L PTR2,A2
ADDQ.B #1,SECTOR ;Next sector
CMPI.B #11,SECTOR ;Are we in the next track?
BLT LB3E4 ;Branch if not -- loop back
MOVE.B #0,SECTOR ;Start next track with sector 0
ADDQ.W #1,TRACKD ;Next track
BRA LB398 ;Loop until all sectors are copied
LB500 MOVEA.L IOPTR,A1
BRA.S LB516 ;Exit
LB50A MOVEA.L IOPTR,A1
MOVE.B #$FF,31(A1) ;Set io_Error (-1)
BRA.S LB500 ;Exit (circuitously)
LB516 MOVEA.L IOPTR,A1 ;Record current track in TRACKS array
MOVE.W 22(A1),D2 ;Get io_Device (unit)
ANDI.W #$0003,D2 ;Mask for safety, legal units: 0..3
ADD.W D2,D2
LEA TRACKS.L,A0 ;Index into TRACK table (words)
MOVE.W TRACK,0(A0,D2.W) ;Set track number for current unit
MOVEQ #0,D0 ;Return error code in D0
MOVE.B 31(A1),D0
MOVEM.L (SP)+,D2/A2-A4 ;Restore registers
RTS
;----------------------------------------------------------------------
;If the track number (D0) is written in the buffer, then A0 is returned
; set equal to PTR1.
; Inputs: track number (D0) and PTR1.
;
SUBRM MOVE.L PTR1,D1 ;Get pointer
TST.L D1 ;Branch if it is zero
BEQ.S SUBRM10
MOVEA.L D1,A0 ;Is the track number in the buffer?
CMP.W 0(A0),D0 ;Return A0 = PTR1
BEQ.S SUBRM99
SUBRM10 MOVEQ #0,D0 ;Return D0 = A0 = 0
MOVEA.L D0,A0
SUBRM99 RTS
;----------------------------------------------------------------------
;Routine to write a track.
; Inputs:
; PTR2 = Pointer to encoded buffer
; 0 = track number
; 2 = bit #0 = dirty/clean flag
; 4 = actual buffer space begins here
;
SUBRO MOVEM.L D2/A2,-(SP)
MOVEA.L PTR2,A2
MOVEQ #1,D0 ;Turn on the motor
BSR DOMOTOR
MOVEQ #0,D0
MOVE.W 0(A2),D0 ;Move to track recorded in buffer
BSR DOSEEK
LEA 4(A2),A0 ;Location of buffer
MOVE.W #13630,D0 ;Write 13630 encoded bytes to a track
BSR WRITTRK ; (13630 = 544 *2 *11 + 1662)
MOVEM.L (SP)+,D2/A2
RTS
;----------------------------------------------------------------------
;Routine to read a track
; Inputs PTR2 = Pointer to buffer
;
SUBRP MOVEM.L A2,-(SP) ;Save A2
MOVEA.L PTR2,A2 ;Point to buffer for encoded data
MOVEQ #1,D0 ;Turn on the motor
BSR DOMOTOR
SUBRP10 MOVEQ #0,D0
MOVE.W TRACKD,D0 ;Move head to desired track
BSR DOSEEK
SUBRP20 LEA $4000-14716(A2),A0 ;Fill bottom of 16K buffer
MOVE.W #14716,D0 ;Number of encoded bytes to read
BSR READTRK ;Read a track
TST.L D0 ;Was there an error?
BEQ.S SUBRP30 ;Branch if not
MOVE.B D0,3(A2)
BRA SUBRP90 ;Exit
SUBRP30
BSR SUBRXQ ;Verify header and checksum
MOVE.B D0,3(A2) ;Save error code
CMPI.B #11,D0
BLO.S SUBRP90 ;Exit on certain errors
ADDQ.B #1,CNTR ;Bump sector counter
MOVE.B CNTR,D0
CMPI.B #10,D0 ;Are all 11 sectors read in?
BGT.S SUBRP90 ;Branch if yes -- exit
ANDI.B #$03,D0
BNE.S SUBRP20 ;Loop for 11 sectors
MOVE.W #-1,TRACK ;We don't know where we are
BRA.S SUBRP10
SUBRP90 MOVEM.L (SP)+,A2 ;Restore A2
RTS
;======================================================================
;Routine to turn the selected drive motor on or off. It returns the
; initial state of the motor (on or off) in D0.
; On = 1; Off = 0.
;
DOMOTOR MOVE.L D2,-(SP)
TST.L D0
SEQ D1
ANDI.B #$0080,D1
MOVE.B CIABBX,D2
ANDI.B #$0080,D2
CMP.B D1,D2
BEQ LB0C2
BCLR #7,CIABBX ;Turn on motor
OR.B D1,CIABBX
MOVEQ #-1,D1
EOR.B D2,D1
MOVE.B D1,CIABB.L
MOVE.B CIABBX,CIABB.L
BTST #7,CIABBX ;Is motor turned on?
BNE LB0C2 ;Branch if not
MOVE.L #500000,D0 ;Wait half a second for motor to
BSR DELAY ; come up to speed
LB0C2
MOVEQ #0,D0
BTST #7,D2
SEQ D0
MOVE.L (SP)+,D2
RTS
;----------------------------------------------------------------------
;Routine to seek the track number in D0.
; D0 returns with error code (= 0 if no errors)
;
DOSEEK MOVEM.L D2-D3,-(SP) ;Save registers
MOVE.W TRACK,D2 ;Get current track number
BPL LB002 ;Branch if it is known
MOVE.L D0,D2 ;Otherwise we don't know where we are
BSR STEPTO0 ;Move head to track 0
TST.L D0 ;Any errors detected?
BNE LB06C ;Branch if so -- exit with error code
EXG D0,D2 ;Get track number back in D0, (D2=0)
LB002
CMP.W D0,D2 ;Is the head already on our track?
BEQ LB06A ;Branch if so -- exit with no errors
MOVE.L D0,D3 ;Save desired track number in D3
MOVE.B CIABBX,D1 ;Get copy of the control port
BSET #2,D1 ;Assume we want the lower head
BTST #0,D3 ;Is this an odd or even track number?
BEQ.S LB020 ;Branch if even -- use lower side
BCLR #2,D1 ;Else, select upper head
LB020
MOVE.B D1,CIABBX ;Update port control bits (and the copy)
MOVE.B D1,CIABB.L
MOVE.W D3,TRACK ;Update current track number
LSR.W #1,D2 ;D2 = current track, D3 = desired track
LSR.W #1,D3 ;Divide by 2 to get cylinder number
SUB.W D3,D2 ;D2 = number of cylinders to move head
BLO.S LB03E ; away from center (think backwards)
BHI.S LB04A
BRA.S LB06A ;We don't need to move the head - exit
LB03E BCLR #1,D1 ;Seek toward center
MOVE.B D1,CIABBX
NEG.W D2 ;Make number of steps positive
BRA.S LB058 ;Enter step loop
LB04A BSET #1,D1 ;Seek toward track 0
MOVE.B D1,CIABBX
BRA.S LB058 ;Enter step loop
LB054 BSR DOSTEP ;Step head in selected direction
LB058 DBF D2,LB054 ;loop for D2 steps
MOVE.L #15000,D0 ;Wait 15 milliseconds for head to settle
BSR DELAY
LB06A MOVEQ #0,D0 ;Indicate no errors
LB06C MOVEM.L (SP)+,D2-D3 ;Restore registers
RTS
;----------------------------------------------------------------------
;Routine to step head to track 0.
; D0 returns with error code (0 = no errors)
;
STEPTO0 MOVEM.L D2,-(SP)
MOVE.B CIABBX,D0
BSET #2,D0 ;Select lower head
BCLR #1,D0 ;Seek toward the center
MOVE.B D0,CIABB.L
MOVE.B D0,CIABBX
MOVEQ #40,D2 ;Set maximum tries
LAF90 BTST #4,CIAA.L ;On track 0?
BNE.S LAFA2 ;Branch if not
BSR DOSTEP
DBF D2,LAF90
LAFA2 BSET #1,CIABBX ;Seek toward track 0
CLR.W TRACK ;Record that we will be on track 0
MOVE.W #100,D2 ;Set maximum steps to 100
BRA.S LAFB6 ;Enter step loop
LAFB2 BSR DOSTEP ;Step head toward track 0
LAFB6 BTST #4,CIAA.L ;On track 0?
DBEQ D2,LAFB2 ;Exit loop if so
MOVE.B CIAA.L,D2
BTST #4,D2 ;On track 0?
BEQ.S LAFD6 ;Branch if so
MOVEQ #30,D0 ;Seek error while verifying posn
BRA.S LAFE2 ;Exit
LAFD6 MOVE.L #15000,D0 ;Wait 15 milliseconds for head to settle
BSR DELAY
MOVEQ #0,D0 ;Indicate no errors
LAFE2 MOVEM.L (SP)+,D2
RTS
;----------------------------------------------------------------------
;Routine to step the head
;
DOSTEP MOVE.B CIABBX,D0
LEA CIABB.L,A1 ;Point to output control port
MOVE.B D0,D1 ;Save copy of original port bits
BCLR #0,D0 ;Command to step the head
MOVE.B D0,(A1) ;Send command
NOP ;Delay for step pulse width
NOP
MOVE.B D1,(A1) ;terminate pulse
MOVE.L #3000,D0 ;Wait at least 3 milliseconds for
BSR DELAY ; step to occur
MOVE.B CIABBX,CIABB.L
RTS
;======================================================================
;Routine to write a track.
; Inputs D0 = number of encoded bytes to write
; A0 = pointer to buffer
; Outputs D0 = error code (0 = no errors)
;
WRITTRK MOVEM.L D2/A2,-(SP)
MOVEA.L A0,A2
MOVE.L D0,D2
MOVE.B CIABBX,CIABB.L
MOVE.W #$4000,DSKLEN.L ;Make sure DMA is off
MOVE.L #1000,D0 ;Wait a millisecond
BSR DELAY
LEA IOBASE.L,A1
MOVE.L A2,DSKPTH-IOBASE(A1) ;Set DMA pointer to buffer
MOVE.W #$1002,INTREQ-IOBASE(A1) ;Clear disk sync & blk done
; BTST #2,CIAA.L ;Has the disk been changed?
; BNE.S LB24A ;Branch if not
BRA.S LB24A ;Ignore disk changed error
LB244 MOVEQ #29,D2 ;io_Error: Disk changed
BRA LB2AE
LB24A BTST #3,CIAA.L ;Is disk write protected?
BEQ LB2BA ;Branch if it is
;Set precomp at 140 ns when TRACK is beyond half way:
MOVE.W #$6000,ADKCON-IOBASE(A1) ;No precomp
MOVE.W TRACK,D1
CMPI.W #81,D1 ;Half way?
BLT.S LB26C ;Branch if lower half
MOVE.W #$A000,D1 ;Precomp 140ns
BRA.S LB270
LB26C MOVE.W #$8000,D1
LB270 MOVE.W D1,ADKCON-IOBASE(A1) ;Set precomp
LSR.W #1,D2 ;Divide by 2 to get number of words
ORI.W #$C000,D2 ;Enable write and DMA
MOVE.W D2,DSKLEN-IOBASE(A1) ;Start DMA
MOVE.W D2,DSKLEN-IOBASE(A1)
WRT30 MOVE.W INTREQR-IOBASE(A1),D0 ;Wait for disk block finished
BTST #1,D0
BEQ.S WRT30
MOVE.L #2000,D0 ;Wait 2 milliseconds
BSR DELAY
MOVE.W #$4000,36(A1) ;Leave DMA in a safe state
; BTST #2,CIAA.L ;Has the disk been changed?
; BEQ.S LB244 ;Branch if so
MOVEQ #0,D2 ;Indicate no errors
LB2AE MOVE.L D2,D0 ;Return error code in D0
MOVEM.L (SP)+,D2/A2
RTS
LB2BA MOVEQ #28,D2 ;io_Error: Write protected
BRA.S LB2AE ;Exit
;----------------------------------------------------------------------
;Routine to read a track.
; Inputs D0 = number of encoded bytes to read
; A0 = pointer to encoded buffer
; Outputs D0 = error code (0 = no errors)
;
READTRK MOVEM.L D2/A2,-(SP) ;Save registers
MOVEA.L A0,A2
MOVE.L D0,D2
MOVE.B CIABBX,CIABB.L
MOVE.W #$4000,DSKLEN.L ;Turn off DMA
MOVE.L #1000,D0 ;Wait a millisecond
BSR DELAY
LEA IOBASE.L,A1
MOVE.L A2,DSKPTH-IOBASE(A1) ;Set pointer to buffer for DMA
MOVE.W #$1002,INTREQ-IOBASE(A1) ;Clear disk sync reg & blk done
; BTST #2,CIAA.L ;Has the disk been changed?
; BNE.S LB1C2 ;Branch if not
BRA.S LB1C2 ;Ignore disk changed
LB1BE MOVEQ #29,D2 ;Error: Disk changed or not present
BRA.S LB1F2 ;Exit with error code
LB1C2
LSR.W #1,D2 ;Divide by 2 to get words
ORI.W #$8000,D2 ;Set DMA enable bit
MOVE.W D2,DSKLEN-IOBASE(A1) ;Command it twice to start DMA
MOVE.W D2,DSKLEN-IOBASE(A1)
RDT30 MOVE.W INTREQR-IOBASE(A1),D0 ;Wait for disk block finished
BTST #1,D0
BEQ.S RDT30
LEA IOBASE.L,A1
MOVE.W #$4000,DSKLEN-IOBASE(A1) ;Prevent accidental writes
; BTST #2,CIAA.L ;Has the disk been changed?
; BEQ.S LB1BE ;Branch if it has -- exit (circuitously)
MOVEQ #0,D2 ;Indicate no errors
LB1F2 MOVE.L D2,D0
MOVEM.L (SP)+,D2/A2 ;Restore registers
RTS
;----------------------------------------------------------------------
;This routine aligns raw read data and verifies the header and checksum.
; It returns an error code in D0 (io_Error).
;
SUBRXQ MOVEM.L D2-D6/A2,-(SP) ;Save registers
LINK A4,#-16 ;Allocate space for local variables
MOVEA.L PTR2,A2
LEA 1664(A2),A2 ;Skip gap
MOVEA.L A2,A0
ADDQ.L #2,A0 ;Skip rest of gap
MOVE.L #1660 +2*544,D0 ;Maximum scan is a gap + a sector
BSR SUBRXO ;Scan for sync bytes
CMPA.L #-1,A0
BEQ ERRX21 ;Couldn't find sync bytes
MOVE.L A0,D5 ;Save pointer to sync table
MOVE.L D0,D2 ;D2 = number of bits to shift
ADDQ.L #8,A0
MOVEQ #9,D4 ;Loop 10 times
MOVEQ #0,D6 ;Initialize checksum to zero
TST.L D2
BNE.S LBC34 ;Branch if bits are shifted
;Otherwise bits are aligned
MOVE.L (A0),-8(A4) ;Save long word after sync
MOVE.L 4(A0),-4(A4) ;Save next long word
MOVE.L #$55555555,D1 ;Compute checksum of header
LBC20 MOVE.L (A0)+,D0
AND.L D1,D0 ;Remove clock bits
EOR.L D0,D6 ;Update "checksum" (really only parity)
DBF D4,LBC20 ;Loop 10 times (= 20 encoded bytes)
MOVE.L (A0)+,-16(A4) ;Get encoded header checksum from
MOVE.L (A0),-12(A4) ; buffer (high, low words)
BRA.S LBC68
LBC34
BSR SUBRXR ;Decode word
MOVE.L D0,-8(A4)
BSR SUBRXR
MOVE.L D0,-4(A4)
MOVEA.L D5,A0
ADDQ.L #8,A0
LBC48 BSR SUBRXR ;Decode word
ANDI.L #$55555555,D0
EOR.L D0,D6
DBF D4,LBC48
BSR SUBRXR
MOVE.L D0,-16(A4)
BSR SUBRXR
MOVE.L D0,-12(A4)
LBC68
LEA -16(A4),A0 ;Point to header checksum
BSR DECOD_L ;Decode 8 bytes to get one long word
CMP.L D0,D6 ;Compare to value computed above
BNE ERRX27 ;Branch if header checksum error
LEA -8(A4),A0 ;Point to format byte, track, sector
BSR DECOD_L ;Decode to get 4 bytes of info
MOVE.L D0,D3 ;Save decoded bytes in D3
MOVE.L D3,-(SP) ; and on the stack
CMPI.B #$FF,0(SP) ;Check for format byte ($FF)
BNE ERRX27 ;Unable to read sector header
MOVE.B 1(SP),D0 ;Check track number
CMP.B TRACKD+1,D0
BNE ERRX27 ;Branch if wrong track
MOVE.L (SP)+,D3
MOVEQ #0,D0
MOVE.B D3,D0 ;Number of sectors until end of write
MULU #544*2,D0 ;Compute offset into encoded data
MOVEA.L D5,A0 ;Point to entry in sync table
MOVEA.L A2,A1 ;Pointer to maximum scan position
MOVE.L D2,D1 ;Index into sync table
MOVE.L D0,D4 ;Offset into encoded data (end of write)
BSR SUBRXS ;Let the blitter align the sectors
MOVEQ #0,D2
MOVE.B D3,D2
SUBI.L #11,D2
NEG.L D2
BEQ.S LBCE6
ADD.L D4,D5
MOVE.L #1660,D0
MOVEA.L D5,A0
ADDQ.L #2,A0
BSR SUBRXO
CMPA.L #-1,A0
BEQ ERRX26 ;Incorrect number of sectors on track
MOVE.L D0,D1 ;Get number of bits of shift (should
MOVEA.L A2,A1 ; be zero)
ADDA.L D4,A1
MOVE.L D2,D0
MULU #544*2,D0
BSR SUBRXS
LBCE6 MOVEA.L A2,A0
ADDA.L D4,A0
BSR SUBRXK
LEA 544*2*11(A2),A0
MOVE.W #$AAA8,D0
BTST #0,-1(A0)
BEQ LBD04
BCLR #15,D0
LBD04
MOVE.W D0,(A0)
MOVE.W #$AAAA,(A2)
MOVEQ #0,D4
MOVEQ #11,D2
MOVE.W D3,D5
LSR.W #8,D5
LBD12 CMPI.L #$2AAAAAAA,0(A2,D4.W) ;2 bytes of MFM encoded 00 data
BEQ.S LBD28
CMPI.L #$AAAAAAAA,0(A2,D4.W)
BNE ERRX22 ;Error in sector preamble
LBD28
CMPI.L #$44894489,4(A2,D4.W) ;sync bytes ($A1 $A1)
BNE ERRX22
LEA 8(A2,D4.W),A0 ;Location of encoded bytes
MOVEQ #40,D1 ;Number of encoded bytes
BSR DOCKSUM ;Compute checksum of header
MOVE.L D0,D6 ;Save checksum in D6
LEA 48(A2,D4.W),A0
BSR DECOD_L ;Decode header checksum
CMP.L D6,D0 ;Compare checksums
BNE ERRX24 ;Branch if header field has bad checksum
LEA 8(A2,D4.W),A0 ;Decode format byte, track and sector
BSR DECOD_L ; numbers, etc.
MOVE.L D0,-(SP)
CMPI.B #$FF,0(SP) ;Format byte must be $FF
BNE ERRX23 ;Error in sector identifier
MOVE.B 1(SP),D1 ;Get track number
CMP.B TRACKD+1,D1
BNE ERRX23 ;Error in sector identifier
MOVE.B 2(SP),D1 ;Get sector number
CMP.B D5,D1
BNE ERRX23 ;Error in sector identifier
MOVE.B D2,3(SP) ;Set number of sectors until end
MOVE.L (SP)+,D0
LEA 8(A2,D4.W),A0 ;Location to store encoded data
BSR ENCOD_L ;Encode and store data
LEA 8(A2,D4.W),A0 ;Location of encoded header data
MOVEQ #40,D1 ;Number of encoded bytes
BSR DOCKSUM ;Compute checksum (D0)
LEA 48(A2,D4.W),A0 ;Encode and store header checksum
BSR ENCOD_L ; at A0
LEA 64(A2,D4.W),A0 ;Location of encoded data
MOVE.W #1024,D1 ;Number of encoded bytes
BSR DOCKSUM ;Compute checksum (D0)
MOVE.L D0,D6 ;Save it in D6
LEA 56(A2,D4.W),A0 ;Decode data checksum that was read in
BSR DECOD_L
CMP.L D6,D0 ;Does it agree with computed checksum?
BNE ERRX25 ;Branch if not -- bad checksum
SUBQ.L #1,D2
ADDQ.B #1,D5
CMPI.B #11,D5
BLT.S LBDC0
MOVEQ #0,D5
LBDC0
ADDI.W #544*2,D4
CMPI.W #544*2*11,D4
BNE LBD12
MOVE.L D3,D1
LSR.L #8,D1
MOVEQ #0,D0
MOVE.B D1,D0
ERRX90 UNLK A4
MOVEM.L (SP)+,D2-D6/A2
RTS
ERRX21 MOVEQ #21,D0 ;Couldn't find sector header
BRA.S ERRX90
ERRX27 MOVEQ #27,D0 ;Unable to read sector header
BRA.S ERRX90
ERRX22 MOVEQ #22,D0 ;Error in sector preamble
BRA.S ERRX90
ERRX23 MOVEQ #23,D0 ;Error in sector identifier
BRA.S ERRX90
ERRX24 MOVEQ #24,D0 ;Header field has bad checksum
BRA.S ERRX90
ERRX25 MOVEQ #25,D0 ;Sector data field has bad checksum
BRA.S ERRX90
ERRX26 MOVEQ #26,D0 ;Incorrect number of sectors on track
BRA.S ERRX90
;======================================================================
;MFM Encode and store the data in D0. The input data (D0) is four bytes,
; and the stored data is eight bytes.
; Inputs:
; D0 = Four bytes of data to be encoded and stored
; A0 = Location to store encoded data (bumped by 8 bytes).
;
ENCOD_L MOVEM.L D2-D3,-(SP)
MOVE.L D0,D3
LSR.L #1,D0 ;Encode the odd bits
BSR SUBRXH
MOVE.L D3,D0 ;Encode the even bits
BSR SUBRXH
BSR SUBRXK
MOVEM.L (SP)+,D2-D3
RTS
;----------------------------------------------------------------------
;MFM Encode the even bits of a long word. The odd bits are used as clock
; bits. A clock bit is set only when the two adjacent data bits are both
; zero.
; Inputs:
; D0 = Data to be MFM encoded (even bits only)
; A0 = Location to store encoded data
;
SUBRXH ANDI.L #$55555555,D0 ;Get the even data bits
MOVE.L D0,D2
EORI.L #$55555555,D2 ;Turn the zeros into ones
MOVE.L D2,D1 ;Copy inverted data into D1
LSL.L #1,D2 ;<-- D2
LSR.L #1,D1 ; D1 -->
BSET #31,D1 ;Assume last bit of last data was a one
AND.L D2,D1 ;Set clock bit if there were two zeros
OR.L D1,D0 ; in a row. Combine clocks with data
BTST #0,-1(A0) ;Check last data bit of preceding data
BEQ.S LB982 ;Branch if it was a zero -- leave clock
BCLR #31,D0 ; bit alone, otherwise it's not needed
LB982
MOVE.L D0,(A0)+ ;Store encoded data in buffer
RTS
;----------------------------------------------------------------------
;Routine to encode and move a block of data (write a sector).
;Inputs:
; D0 = Number of bytes to encode
; A0 = Pointer to bytes to be encoded
; A1 = Pointer to location to hold encoded bytes
;
ENCODE LINK A2,#-30 ;Reserve space for blit structure
MOVE.W D0,D1
LSL.W #2,D1 ;Number of bytes *2
ORI.W #$0008,D1 ; add 8 (BLITSIZE)
MOVE.W D1,-10(A2)
MOVEM.L D0/A0-A1,-22(A2)
MOVE.L A3,-4(A2)
LEA IOBASE.L,A1
ENC20 MOVE.W DMACONR-IOBASE(A1),D0 ;Wait for blitter not busy
BTST #14,D0
BNE.S ENC20
MOVE.W #$0040,INTREQ-IOBASE(A1) ;Clear blitter finished bit
MOVEA.L #$DFF000,A0
LEA -30(A2),A1 ;Point to blit structure
BSR SUBRXZ
LEA IOBASE.L,A1
ENC30 MOVE.W INTREQR-IOBASE(A1),D0 ;Wait for blitter finished
BTST #6,D0
BEQ.S ENC30
LEA IOBASE.L,A1
ENC40 MOVE.W DMACONR-IOBASE(A1),D0 ;Wait for blitter not busy
BTST #14,D0
BNE.S ENC40
MOVE.W #$0040,INTREQ-IOBASE(A1) ;Clear blitter finished bit
MOVEA.L #$DFF000,A0
LEA -30(A2),A1 ;Point to blit structure
BSR SUBRY
LEA IOBASE.L,A1
ENC50 MOVE.W INTREQR-IOBASE(A1),D0 ;Wait for blitter finished
BTST #6,D0
BEQ.S ENC50
LEA IOBASE.L,A1
ENC60 MOVE.W DMACONR-IOBASE(A1),D0 ;Wait for blitter not busy
BTST #14,D0
BNE.S ENC60
MOVE.W #$0040,INTREQ-IOBASE(A1) ;Clear blitter finished bit
MOVEA.L #$DFF000,A0
LEA -30(A2),A1 ;Point to blit structure
BSR SUBRZ
LEA IOBASE.L,A1
ENC70 MOVE.W INTREQR-IOBASE(A1),D0 ;Wait for blitter finished
BTST #6,D0
BEQ.S ENC70
LEA IOBASE.L,A1
ENC80 MOVE.W DMACONR-IOBASE(A1),D0 ;Wait for blitter not busy
BTST #14,D0
BNE.S ENC80
MOVE.W #$0040,INTREQ-IOBASE(A1) ;Clear blitter finished bit
MOVEA.L #$DFF000,A0
LEA -30(A2),A1 ;Point to blit structure
BSR SUBRAA
LEA IOBASE.L,A1
ENC90 MOVE.W INTREQR-IOBASE(A1),D0 ;Wait for blitter finished
BTST #6,D0
BEQ.S ENC90
MOVEM.L -22(A2),D0/A0-A1
MOVE.L D0,D1
MOVEA.L A1,A0
BSR SUBRXK
ADDA.L D1,A0
BSR SUBRXK
ADDA.L D1,A0
BSR SUBRXK
UNLK A2 ;Release local variables
RTS
;----------------------------------------------------------------------
;Routine to handle the blitter.
; Inputs:
; A0 = $DFF000
; A1 = Pointer to blit structure (node)
;
SUBRXZ MOVE.L A5,-(SP)
MOVEA.L A1,A5
BSR SETBLIT
MOVEA.L A5,A1
MOVEM.L 8(A1),D0-D1/A5
MOVE.L D1,76(A0)
MOVE.L D1,80(A0)
MOVE.L A5,84(A0) ;Disk resource
MOVE.W #$1DB1,BLTCON0-IOBASE(A0)
MOVE.W #$0000,BLTCON1-IOBASE(A0)
MOVE.W 20(A1),BLTSIZE-IOBASE(A0)
MOVEA.L (SP)+,A5
RTS
;----------------------------------------------------------------------
;Routine to handle the blitter.
; Inputs:
; A0 = $DFF000
; A1 = Pointer to blit structure (node)
;
;
SUBRY MOVE.L A5,-(SP)
MOVEM.L 8(A1),D0-D1/A5
MOVE.L A5,76(A0)
MOVE.L D1,80(A0)
MOVE.L A5,84(A0)
MOVE.W #$2D8C,BLTCON0-IOBASE(A0)
MOVE.W 20(A1),BLTSIZE-IOBASE(A0)
MOVEA.L (SP)+,A5
RTS
;----------------------------------------------------------------------
;Routine to handle the blitter.
; Inputs:
; A0 = $DFF000
; A1 = Pointer to blit structure (node)
;
;
SUBRZ MOVE.L A5,-(SP)
MOVEM.L 8(A1),D0-D1/A5
ADD.L D0,D1
SUBQ.L #2,D1
ADDA.L D0,A5
ADDA.L D0,A5
SUBQ.L #2,A5
MOVE.L D1,76(A0)
MOVE.L D1,80(A0)
MOVE.L A5,84(A0)
MOVE.W #$0DB1,BLTCON0-IOBASE(A0)
MOVE.W #$1002,BLTCON1-IOBASE(A0)
MOVE.W 20(A1),BLTSIZE-IOBASE(A0)
MOVEA.L (SP)+,A5
RTS
;----------------------------------------------------------------------
;Routine to handle the blitter.
; Inputs:
; A0 = $DFF000
; A1 = Pointer to blit structure (node)
;
SUBRAA MOVE.L A5,-(SP) ;Save A5
MOVEM.L 8(A1),D0-D1/A5
ADDA.L D0,A5
MOVE.L A5,76(A0)
MOVE.L D1,80(A0)
MOVE.L A5,84(A0)
MOVE.W #$1D8C,BLTCON0-IOBASE(A0)
MOVE.W #$0000,BLTCON1-IOBASE(A0)
MOVE.W 20(A1),BLTSIZE-IOBASE(A0) ;Set size and go
MOVEA.L (SP)+,A5 ;Restore A5
RTS
;----------------------------------------------------------------------
;
SUBRXK MOVE.B (A0),D0
BTST #0,-1(A0)
BNE.S LB9CC
BTST #6,D0
BNE.S LB9D2
BSET #7,D0
BRA.S LB9D0
LB9CC BCLR #7,D0
LB9D0 MOVE.B D0,(A0)
LB9D2 RTS
;======================================================================
;Decode an MFM-encoded, 8-byte block of data pointed to by A0.
; Return the long word result in D0.
;
DECOD_L MOVE.L (A0)+,D0 ;Get odd bits
MOVE.L (A0)+,D1 ;Get even bits
ANDI.L #$55555555,D0 ;Mask off the clock bits
ANDI.L #$55555555,D1
LSL.L #1,D0 ;Shift odd bits into position
OR.L D1,D0 ;Combine with even bits
RTS ;Return with result in D0
;----------------------------------------------------------------------
;Routine to decode and move a block of data (i.e. read sector).
; Inputs: D0 = Number of bytes to decode (512, number after decoding)
; A0 = Pointer to decoded buffer (io_Data)
; A1 = Pointer to encoded track buffer data
;
;This routine uses the blitter. The following is the blit structure
; formerly used by the graphics library routine QBlit:
;
; A1 --> -30 Pointer to next blit node
; -26 SUBREE (not used)
; -22 D0 call clean up routine?
; -21 (dummy)
; -20 blit size
; -18 A0 beamsync
; -16 clean up
; -14 A1
; -10 D0 *4 ! 8
; -6
; -4 A3
; A2 --> 0
;
;
DECODE LINK A2,#-30 ;Reserve space for local variables
MOVEM.L D0/A0-A1,-22(A2) ;Save registers in this blit structure
LSL.W #2,D0 ;*4
ORI.W #$0008,D0
MOVE.W D0,-10(A2)
MOVE.L A3,-4(A2)
LEA IOBASE.L,A1
DEC20 MOVE.W DMACONR-IOBASE(A1),D0 ;Wait for blitter not busy
BTST #14,D0
BNE.S DEC20
MOVE.W #$0040,INTREQ-IOBASE(A1) ;Clear blitter finished bit
MOVEA.L #$DFF000,A0
LEA -30(A2),A1 ;Set pointer to blit structure
BSR SUBREE
LEA IOBASE.L,A1
DEC30 MOVE.W INTREQR-IOBASE(A1),D0 ;Wait for blitter finished
BTST #6,D0
BEQ.S DEC30
MOVEM.L -22(A2),D0/A0-A1 ;Restore registers and stack
UNLK A2 ;Release local variable space
RTS
;----------------------------------------------------------------------
;Routine to handle the blitter.
; Inputs:
; A0 = $DFF000
; A1 = Pointer to blit structure (node)
;
SUBREE MOVE.L A5,-(SP)
MOVEA.L A1,A5
BSR SETBLIT
MOVEA.L A5,A1
MOVEM.L 8(A1),D0-D1/A5
ADDA.L D0,A5
SUBQ.L #1,A5
MOVE.L A5,BLTAPTH-IOBASE(A0)
ADDA.L D0,A5
MOVE.L A5,BLTBPTH-IOBASE(A0)
ADD.L D0,D1
SUBQ.L #1,D1
MOVE.L D1,BLTDPTH-IOBASE(A0)
MOVE.W #$1DD8,BLTCON0-IOBASE(A0)
MOVE.W #$0002,BLTCON1-IOBASE(A0)
MOVE.W 20(A1),BLTSIZE-IOBASE(A0) ;Set size and go
MOVEA.L (SP)+,A5
RTS
;======================================================================
;Routine to scan for sync bytes at start of sector. These sync bytes
; (A1A1, encoded as 44894489) may be shifted any number of bit positions
;Inputs:
; D0 = Maximum number of encoded words to scan (2748/1660).
; A0 = Pointer to encoded data (at start of header).
;Outputs:
; D0 = Number of bits of shift.
; A0 = Points to table entry before the entry that matches the
; sync byte.
;
SUBRXO MOVEM.L D2-D4/A2,-(SP)
MOVE.W #$AAAA,D3 ;There are two possible patterns for
MOVE.W #$5555,D4 ; nulls depending on phase lock
MOVEA.L A0,A2 ;Point A2 to maximum scan position
ADDA.L D0,A2
LBB54 MOVE.W (A0)+,D2 ;Get word from buffer
CMP.W D3,D2 ;Is it $AAAA?
BEQ.S LBB90 ;Branch if it is
CMP.W D4,D2 ;Is it $5555?
BEQ.S LBB68 ;Branch if it is
CMPA.L A0,A2 ;Loop if we haven't gone too far
BHI.S LBB54
LBB62 MOVEQ #-1,D0 ;Indicate error
MOVEA.L D0,A0 ;A0 = -1
BRA.S LBB8A ;Exit with error
;Found a $5555:
LBB68 MOVEQ #15,D0 ;Initialize counter for table entries
LEA TBLEVEN.L,A1 ;Point to table containing even bits
LBB70 CMPA.L A0,A2 ;Have we scanned too far?
BLS.S LBB62 ;Branch if we have -- error exit
MOVE.W (A0)+,D1 ;Scan past nulls
CMP.W D2,D1
BEQ.S LBB70
SUBQ.L #2,A0 ;Back up a word
MOVE.L (A0),D1 ;Fetch long word from this location
LBB7E CMP.L (A1)+,D1 ;Compare it to entries in the table
BEQ.S LBB88 ;Exit loop if found
SUBQ.L #2,D0 ;Decrement counter
BGE.S LBB7E ;Loop through table
BRA.S LBB54 ;Not found -- try again
LBB88 SUBQ.L #4,A0 ;Adjust A0 to point to matching entry
LBB8A MOVEM.L (SP)+,D2-D4/A2
RTS
LBB90 MOVEQ #14,D0
LEA TBLODD.L,A1
BRA.S LBB70
;These tables contain sync byte patterns for all possible bit shifts.
TBLEVEN DC.L $2244A244
DC.L $48912891
DC.L $52244A24
DC.L $54891289
DC.L $552244A2
DC.L $55489128
DC.L $5552244A
DC.L $55548912
TBLODD DC.L $91225122
DC.L $A4489448
DC.L $A9122512
DC.L $AA448944
DC.L $AA912251
DC.L $AAA44894
DC.L $AAA91225
DC.L $44894489 ;Normal unshifted position
;----------------------------------------------------------------------
;Decode word at A0 and return it in D0.
;Inputs:
; A0 = Pointer to encoded buffer
; D2 = Number of bits to shift
;
SUBRXR MOVE.L (A0)+,D0
MOVE.W (A0),D1
MOVEQ #16,D3
SUB.L D2,D3
LSL.L D3,D0 ;D0:= D0 *
LSR.W D2,D1
OR.W D1,D0
RTS
;----------------------------------------------------------------------
;Routine to call the blitter handling routine.
; Inputs:
; D0 = Byte offset into encoded buffer
; D1 = Index into sync table
; A2 = Pointer to max offset in buffer
;
; A1 --> 0 -30
; 4 -26 SUBRXT (not used)
; 8 -22 D0
; 12 -18 A0
; 16 -14 A1 = pointer to max posn
; 20 -10 BlitSize
; 22 -8 Index into sync table
; 24 -6 Data (word) at max scan posn
; 26 -4 A3
; A2 --> 30 0
;
;
SUBRXS LINK A2,#-30 ;Reserve space for local variables
MOVE.B D1,-8(A2)
TST.L D1
BEQ.S LBE1A ;Branch if no bit shift required
ADDQ.L #2,D0 ;Adjust byte offset
LBE1A
MOVE.L D0,D1
ADDI.L #63,D1
ANDI.W #$FFC0,D1
ORI.W #$0020,D1
MOVE.W D1,-10(A2)
MOVEM.L D0/A0-A1,-22(A2)
MOVE.L A3,-4(A2)
LEA IOBASE.L,A1
SXS20 MOVE.W DMACONR-IOBASE(A1),D0 ;Wait for blitter not busy
BTST #14,D0
BNE.S SXS20
MOVE.W #$0040,INTREQ-IOBASE(A1) ;Clear blitter finished bit
MOVEA.L #$DFF000,A0
LEA -30(A2),A1 ;Point to blit structure
BSR SUBRXT
LEA IOBASE.L,A1
SXS30 MOVE.W INTREQR-IOBASE(A1),D0 ;Wait for blitter finished
BTST #6,D0
BEQ.S SXS30
LEA -30(A2),A1 ;Point to blit structure
TST.B 22(A1) ;Test shift count
BEQ.S SXS40 ;Branch if no shift
MOVEA.L 16(A1),A0 ;Get pointer to max position
MOVE.W 24(A1),-2(A0) ;Restore data
SXS40
UNLK A2 ;Release local variable space
RTS
;----------------------------------------------------------------------
;Routine to handle the blitter.
; Inputs:
; A0 = $DFF000
; A1 = Pointer to blit structure (node)
;
SUBRXT MOVE.L A5,-(SP) ;Save A5
MOVEA.L A1,A5 ;Save pointer to blit structure in A5
BSR SETBLIT ;Set blitter registers
MOVE.B 22(A5),D0 ;Get index into sync table
MOVEQ #12,D1 ;Move shift count into high nybble
LSL.W D1,D0
MOVE.W #$05CC,BLTCON0-IOBASE(A0) ;No shift, B = D
MOVE.W D0,BLTCON1-IOBASE(A0) ;Set shift value for B source
MOVEM.L 8(A5),D0-D1/A1 ;Restore registers from SUBRXS
MOVE.L D1,BLTBPTH-IOBASE(A0)
TST.B 22(A5) ;Get index into sync table
BEQ.S LBE94 ;Branch if shift count = 0
SUBQ.L #2,A1
LBE94
MOVE.L A1,BLTDPTH-IOBASE(A0) ;Max scan positon
MOVE.W (A1),24(A5)
MOVE.W 20(A5),BLTSIZE-IOBASE(A0) ;Set size and go
MOVEA.L (SP)+,A5 ;Restore A5
RTS
;----------------------------------------------------------------------
;Set up blitter registers.
; Inputs:
; A0 = $DFF000
;
;
SETBLIT LEA BLTAFWM-IOBASE(A0),A1 ;Set first word mask to all ones
MOVE.L #$FFFFFFFF,(A1) ; i.e. no masking
MOVEQ #0,D0
LEA BLTBMOD-IOBASE(A0),A1
MOVE.L D0,(A1)+ ;BLTBMOD, BLTAMOD, modulo := 0
MOVE.W D0,(A1)+ ;BLTDMOD
ADDQ.L #8,A1
MOVE.W #$5555,(A1) ;BLTCDAT
RTS
;======================================================================
;Routine to delay at least D0 microseconds. (Be aware of interrupts and
; DMA slowing things down. Also be aware that this may not work if a
; faster processor is used.) This will work for up to 16 seconds of
; delay.
;
DELAY MOVE.L D2,-(SP) ;Save D2
MOVE.L D0,D2 ;Get copy of the delay time in D2
LSR.L #8,D2 ;Divide it by 256
DLY10 MOVE.L #256,D0 ;Set D0 to delay 256 us
LSR.W #1,D0 ;Adjust D0 for delay loop
MOVE.L D0,D1
LSR.W #1,D1
ADD.L D1,D0
LSR.W #2,D1
SUB.L D1,D0
MOVE.W D0,D1
SWAP D0
DLY20 DBF D1,@ ;Kill 10 cycles * D1 / 6.7 (microsec)
MOVE.W #$FFFF,D1
SUBQ.W #1,D0
BPL.S DLY20
DBF D2,DLY10 ;Loop for each 256 us interval
MOVE.L (SP)+,D2 ;Restore D2
RTS
;----------------------------------------------------------------------
;Routine to compute the "checksum" of encoded data.
; Inputs:
; D1 = Number of bytes of encoded data
; A0 = Location of the bytes
; Outputs:
; D0 = The 32-bit "checksum"
;
DOCKSUM MOVE.L D2,-(SP) ;Save D2
LSR.W #2,D1 ;Divide by 4 to get long words
SUBQ.W #1,D1 ;Compensate for DBF which includes 0
MOVEQ #0,D0 ;Initialize checksum
DOCK10 MOVE.L (A0)+,D2 ;EOR the bits (parity style)
EOR.L D2,D0
DBF D1,DOCK10
ANDI.L #$55555555,D0 ;Mask off the MFM clock bits
MOVE.L (SP)+,D2 ;Restore D2
RTS
END EQU @-1 ;ADDRESS OF END OF HANDLER
;======================================================================
;Hook this handler into the unit-handler tables
;
ORG 4 *UNTNUM +MAXTBL
DC.L 3520
DC.L 3520
ORG 4 *UNTNUM +OFFTBL
DC.L 0
DC.L 0
ORG 4 *UNTNUM +UNTTBL
DC.L FLOPHAN
DC.L FLOPHAN
END
3520
DC.L 3520
ORG 4 *U